package utils

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/config"
	"github.com/dgrijalva/jwt-go"
)

type JwtClaims struct {
	UserId string
}

var (
	verifyKey  string
	ErrAbsent  = "token absent"  // 令牌不存在
	ErrInvalid = "token invalid" // 令牌无效
	ErrExpired = "token expired" // 令牌过期
	ErrOther   = "other error"   // 其他错误
)

func init() {

	jwtConfigPath := filepath.Join(beego.AppPath, "conf", "jwt.ini")
	jwtConfig, err := config.NewConfig("ini", jwtConfigPath)
	if err != nil {
		beego.Error("new jwt config error", err.Error())
		return
	}

	envRunMode := os.Getenv("CPS_RUNMODE")
	if envRunMode == "" {
		envRunMode = beego.DEV
	}

	verifyKey = jwtConfig.String(envRunMode + "::token")
}

func JwtGetToken(jwtClaims JwtClaims) (string, error) {
	claims := make(jwt.MapClaims)
	claims["UserId"] = jwtClaims.UserId
	claims["exp"] = time.Now().Add(time.Hour * 480).Unix() //20天有效期，过期需要重新登录获取token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(verifyKey))
	if err != nil {
		log.Println(err)
	}
	return tokenString, err
}

func JwtValidateToken(tokenString string) (bool, error) {
	if tokenString == "" {
		return false, errors.New(ErrAbsent)
	}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(verifyKey), nil
	})

	if token == nil {
		return false, errors.New(ErrInvalid)
	}
	if token.Valid {

		return true, nil
	} else if ve, ok := err.(*jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			return false, errors.New(ErrInvalid)
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			return false, errors.New(ErrExpired)
		} else {
			return false, errors.New(ErrOther)
		}
	} else {
		return false, errors.New(ErrOther)
	}
}

func JwtToUser(tokenString string) (string, error) {
	if tokenString == "" {
		return "", errors.New(ErrAbsent)
	}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(verifyKey), nil
	})

	if token == nil {
		return "", errors.New(ErrInvalid)
	}
	if token.Valid {
		claims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			return "", errors.New(ErrInvalid)
		}
		if claims == nil {
			return "", errors.New(ErrInvalid)
		}
		return claims["UserId"].(string), nil
	} else if ve, ok := err.(*jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			return "", errors.New(ErrInvalid)
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			return "", errors.New(ErrExpired)
		} else {
			return "", errors.New(ErrOther)
		}
	} else {
		return "", errors.New(ErrOther)
	}
}
